#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
author:lili
date:20190422
lfm model train main function
"""

import numpy as np
import sys
sys.path.append('/home/lili/project/recommend/pyrcd/personal_recommendation')
import LFM.util.read as read
import operator


def lfm_train(train_data, F, alpha, beta, step):
    """
    Args:
        train_data: train_data for lfm 要训练的数据 
        F: user vector len, item vector len 维度
        alpha:regularization factor 正则化参数
        beta: learning rate 学习率
        step: iteration num 迭代的次数
    Return:
        dict: key itemid, value:np.ndarray
        dict: key userid, value:np.ndarray
    """
    user_vec = {}
    item_vec = {}
    for step_index in range(step):
        for data_instance in train_data:
            userid, itemid, label = data_instance
            if userid not in user_vec:
                user_vec[userid] = init_model(F)
            if itemid not in item_vec:
                item_vec[itemid] = init_model(F)
            delta = label - model_predict(user_vec[userid], item_vec[itemid])  # 视频讲解中此处代码有误，应该每个样本都更新，余弦cos
            for index in range(F):
                user_vec[userid][index] += beta *(delta*item_vec[itemid][index] - alpha*user_vec[userid][index]) #梯度收敛
                item_vec[itemid][index] += beta*(delta*user_vec[userid][index] - alpha*item_vec[itemid][index]) #梯度收敛
        beta = beta * 0.9
    # print user_vec
    # print item_vec
    #返回收敛出来的矩阵，矩阵uf 和 矩阵 fi,分解矩阵
    return user_vec, item_vec


def init_model(vector_len):
    """
    Args:
        vector_len: the len of vector
    Return:
         a ndarray 正态分布的数组
    """
    return np.random.randn(vector_len)


def model_predict(user_vector, item_vector):
    """
    user_vector and item_vector distance cos值，现在用的是余弦
    Args:
        user_vector: model produce user vector
        item_vector: model produce item vector
    Return:
         a num
    """
    #向量的乘积/向量的范数积
    res = np.dot(user_vector, item_vector)/(np.linalg.norm(user_vector)*np.linalg.norm(item_vector))
    return res


def model_train_process():
    """
    test lfm model train
    """
    train_data=read.get_train_data("../data/ratings.txt")
    user_vec, item_vec = lfm_train(train_data, 50, 0.01, 0.1, 50)
    # print user_vec
    for userid in user_vec:
        recom_result = give_recom_result(user_vec, item_vec, userid)
        print recom_result
        # ana_recom_result(train_data, userid, recom_result)

#返回特定uid的推荐列表
def model_train_process_uid(userid):
    """
    test lfm model train
    Return:
        a dic:key ,user,[(itemid, score), (itemid1, score1)]
    """
    # train_data=read.get_train_data("../data/ratings.txt")
    # user_vec, item_vec = lfm_train(train_data, 50, 0.01, 0.1, 50)
    # np.save('/home/lili/project/recommend/ll_newscms/public/data/lfmuser.npy', user_vec)
    # np.save('/home/lili/project/recommend/ll_newscms/public/data/lfmitem.npy', item_vec)
    user_vec = np.load('/home/lili/project/recommend/ll_newscms/public/data/lfmuser.npy').item() #模型获取数据
    item_vec = np.load('/home/lili/project/recommend/ll_newscms/public/data/lfmitem.npy').item()  #模型获取数据

    # print user_vec
    recom_result = give_recom_result(user_vec, item_vec, userid)
    rr={}
    rr[userid]=recom_result
    # print rr
    return rr



#训练数据，保存模型
def train_to_file():
    train_data = read.get_train_data("/home/lili/project/recommend/ll_newscms/public/data/rating.txt")
    # print train_data
    user_vec, item_vec = lfm_train(train_data, 50, 0.01, 0.1, 50)
    np.save('/home/lili/project/recommend/ll_newscms/public/data/lfmuser.npy', user_vec)
    np.save('/home/lili/project/recommend/ll_newscms/public/data/lfmitem.npy', item_vec)


def give_recom_result(user_vec, item_vec, userid):
    """
    use lfm model result give fix userid recom result
    Args:
        user_vec: lfm model result
        item_vec:lfm model result
        userid:fix userid
    Return:
        a list:[(itemid, score), (itemid1, score1)]
    """
    fix_num = 100
    if userid not in user_vec:
        return []
    record = {}
    recom_list = {}
    user_vector = user_vec[userid] #指定的用户向量
    for itemid in item_vec:
        item_vector = item_vec[itemid]
        res = np.dot(user_vector, item_vector)/(np.linalg.norm(user_vector)*np.linalg.norm(item_vector))
        record[itemid] = res
    #排序和小数精确3位
    for zuhe in sorted(record.iteritems(), key= operator.itemgetter(1), reverse=True)[:fix_num]:
        itemid = zuhe[0]
        score = round(zuhe[1], 3)
        recom_list[itemid] = score
    return recom_list


def ana_recom_result(train_data, userid, recom_list):
    """
    debug recom result for userid
    Args:
        train_data: train data for lfm model
        userid:fix userid
        recom_list: recom result by lfm
    """
    item_info = read.get_item_info("../data/movies.txt")
    for data_instance in train_data:
        tmp_userid, itemid, label = data_instance
        if tmp_userid == userid and label == 1:
            print item_info[itemid]
    print "recom result"
    for zuhe in recom_list:
        print item_info[zuhe[0]]

#web返回的接口
def web_return(uid):
    recom_result = model_train_process_uid(str(uid))
    return recom_result

if __name__ == "__main__":
    # model_train_process()
    train_to_file()
    # print web_return('20')
